package ikudyk.university;

import java.util.LinkedList;
import java.util.Scanner;
import java.util.regex.Pattern;

public class Token {
    private String value;
    private int type;

    private void setType(int tokenType) {
        this.type = tokenType;
    }

    public static final int OPERATOR = 1;
    public static final int NUMBER = 2;
    public static final int BOOLEAN = 3;
    public static final int BRACKET_OPEN = 4;
    public static final int BRACKET_CLOSE = 5;
    public static final int WHITESPACES = 6;
    public static final int BAD_TOKEN = -1;


    static public final String OPERATOR_REGEX = getOperatorRegex();
    public static final String NUMBER_REGEX = "((0|([1-9][0-9]*))([.][0-9]+)?)|(R_([a-zA-Z0-9])+)";
    public static final String BOOLEAN_REGEX = "([Ff][Aa][Ll][Ss][Ee])|([Tt][Rr][Uu][Ee])|(B_([a-zA-Z0-9])+)";
    public static final String BRACKET_OPEN_REGEX = "[(]";
    public static final String BRACKET_CLOSE_REGEX = "[)]";
    static public final String WHITESPACES_REGEX = "(\\s)+";

    static public final String UNIVERSAL_TOKEN_REGEX =
            "(" + BRACKET_OPEN_REGEX + ")|"
            + "(" + BRACKET_CLOSE_REGEX + ")|"
            + "(" + NUMBER_REGEX + ")|"
            + "(" + BOOLEAN_REGEX + ")|"
            + "(" + OPERATOR_REGEX + ")|"
            + "(" + WHITESPACES_REGEX + ")";

    public Token(String value) {
        this.value = value;
        if(value.matches(OPERATOR_REGEX)) {
            type = OPERATOR;
        } else if(value.matches(NUMBER_REGEX)) {
            type = NUMBER;
        } else if(value.matches(BOOLEAN_REGEX)) {
            type = BOOLEAN;
        } else if(value.matches(BRACKET_OPEN_REGEX)) {
            type = BRACKET_OPEN;
        } else if(value.matches(BRACKET_CLOSE_REGEX)) {
            type = BRACKET_CLOSE;
        } else if(value.matches(WHITESPACES_REGEX)) {
            type = WHITESPACES;
        } else {
            type = BAD_TOKEN;
        }
    }

    public String getValue() { return value; }
    public int getType() {  return type; }

    static public String getOperatorRegex() {
        int operatorQuantity = Operator.operators.length;
        String operatorRegex = "";
        for(int i = 0; i < operatorQuantity; i++) {
            if(i != 0) {
                operatorRegex += "|";
            }
            // \\Q, \\E solve metacharacters issue
            operatorRegex += "(\\Q" + Character.toString(Operator.operators[i].getSymbol()) + "\\E)";
        }
        return operatorRegex;
    }

    public static LinkedList<Token> tokenizeString(String expression) throws Exception {
        Scanner scanner = new Scanner(expression);
        LinkedList<Token> result = new LinkedList<Token>();
        boolean finished = false;
        String token;
        if(expression.split(UNIVERSAL_TOKEN_REGEX).length != 0) {
            throw new Exception("Untokenizable expression: " + expression);
        }
        while(!finished) {
            token = scanner.findInLine(Pattern.compile(UNIVERSAL_TOKEN_REGEX));
            if(token == null) {
                finished = true;
            } else {
                result.add(new Token(token));
            }
        }
        scanner.close();
        return result;
    }

}
